/**
 * 
 */
package datamodel;

import java.util.Observable;

/**
 * one single GridElement<br>
 * provides information over its state
 * 
 * @author Jakob Karolus, Kevin Munk
 * @version 1.0
 * 
 */
public class GridElement extends Observable {

	private GridElementState state;
	private GridElementAlgoState algoState;
	private int row;
	private int column;
	private int distance;
	
	//TODO: OPTIONAL add attributes if necessary
	
	// the precursor of the GridElement in order to reconstruct the path
	GridElement precursor;
	
	
	//f cost
	private int heuristic;
	//h host
	private int estimateDistance;
	
	protected static GridElement currentEnd;
	protected static GridElement currentStart;

	/**
	 * constructor
	 * sets state to GridElementState.FREE and the distance to -1
	 * 
	 * @param row
	 * @param column
	 */
	public GridElement(int row, int column) {
		this.row = row;
		this.column = column;
		this.setState(GridElementState.FREE);
		this.algoState = GridElementAlgoState.NONE;
		this.distance = -1;
		
		this.precursor = null;
		this.setHeuristic(Integer.MAX_VALUE);
		this.setEstimateDistance(Integer.MAX_VALUE);
	}

	/**
	 * constructor
	 * sets the distance to -1
	 * 
	 * @param state
	 *            the state to set
	 * @param row
	 * @param column
	 */
	public GridElement(int row, int column, GridElementState state) {
		this(row, column);
		this.setState(state);
		this.algoState = GridElementAlgoState.NONE;

	}

	/**
	 * @param state
	 *            the state to set
	 */
	public void setState(GridElementState state) {

		if (state == GridElementState.START) {
			if (currentStart != null)
				currentStart.setState(GridElementState.FREE);
			currentStart = this;
		} else if (state == GridElementState.END) {
			if (currentEnd != null)
				currentEnd.setState(GridElementState.FREE);
			currentEnd = this;
		}
		this.state = state;


		this.setChanged();
		this.notifyObservers(new UpdateEvent(UpdateEvent.ELEMENT_CHANGED,
				this.row, this.column, state, null));
	}

	/**
	 * @return the state
	 */
	public GridElementState getState() {
		return state;
	}

	/**
	 * @return the row
	 */
	public int getRow() {
		return row;
	}

	/**
	 * @return the column
	 */
	public int getColumn() {
		return column;
	}

	/**
	 * @param distance
	 *            the distance to set
	 */
	public void setDistance(int distance) {
		this.distance = distance;
		
		this.setAlgoState(GridElementAlgoState.LOOKED_AT);
	}

	/**
	 * @return the distance
	 */
	public int getDistance() {
		return distance;
	}
	
	public GridElement getPrecursor(){
		return this.precursor;
	}
	
	public void setPrecursor(GridElement element){
		this.precursor = element;
	}

	/**
	 * @param algoState
	 *            the algoState to set
	 */
	public void setAlgoState(GridElementAlgoState algoState) {
		this.algoState = algoState;

		this.setChanged();
		this.notifyObservers(new UpdateEvent(UpdateEvent.ELEMENT_CHANGED,
				this.row, this.column, null, algoState));
	}

	/**
	 * @return the algoState
	 */
	public GridElementAlgoState getAlgoState() {
		return algoState;
	}

	/**
	 * Resets the element. sets distance to -1, the algo state to NONE and the
	 * bestNeighbor to null.
	 */
	public void resetElement() {
		this.setDistance(-1);
		this.setAlgoState(GridElementAlgoState.NONE);
	}
	
	/**
	 * resets the class variables
	 */
	public static void resetStartEnd(){
		GridElement.currentStart = null;
		GridElement.currentEnd = null;
	}

	/**
	 * @param estimateDistance the estimateDistance to set
	 */
	public void setEstimateDistance(int estimateDistance) {
		this.estimateDistance = estimateDistance;
	}

	/**
	 * @return the estimateDistance
	 */
	public int getEstimateDistance() {
		return estimateDistance;
	}

	/**
	 * @param heuristic the heuristic to set
	 */
	public void setHeuristic(int heuristic) {
		this.heuristic = heuristic;
	}

	/**
	 * @return the heuristic
	 */
	public int getHeuristic() {
		return heuristic;
	}

}
